/*
 *  Copyright (c) WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.wso2.balana.samples.kmarket.trading;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.wso2.balana.Balana;
import org.wso2.balana.PDP;
import org.wso2.balana.PDPConfig;
import org.wso2.balana.ParsingException;
import org.wso2.balana.ctx.AbstractResult;
import org.wso2.balana.ctx.AttributeAssignment;
import org.wso2.balana.ctx.ResponseCtx;
import org.wso2.balana.finder.AttributeFinder;
import org.wso2.balana.finder.AttributeFinderModule;
import org.wso2.balana.finder.impl.FileBasedPolicyFinderModule;
import org.wso2.balana.xacml3.Advice;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * On-line trading sample
 */
public class KMarketAccessControl {

	private static Balana balana;

	private static Map<String, String> priceMap = new HashMap<String, String>();

	private static Map<String, String> idMap = new HashMap<String, String>();

	private static String products = "[1] Food\t[2] Drink\t[3] Fruit\t[4] Liquor\t [5] Medicine";

	public static void main(String[] args) {

		Scanner console;
		String userName = null;
		String productName = null;
		int numberOfProducts = 1;
		int totalAmount = 0;

		printDescription();

		initData();

		initBalana();

		System.out
				.println("\nYou can select one of following item for your shopping chart : \n");

		System.out.println(products);

		if ((console = new Scanner(System.in)) != null) {

			System.out.println("Enter User name : ");
			userName = console.nextLine();
			if (userName == null || userName.trim().length() < 1) {
				System.err.println("\nUser name can not be empty\n");
				return;
			}

			System.out.print("Enter Product Id : ");

			String productId = console.nextLine();
			if (productId == null || productId.equals("")) {
				System.err.println("\nProduct Id can not be empty\n");
				return;
			} else {
				productName = idMap.get(productId);
				if (productName == null) {
					System.err.println("\nEnter valid product Id\n");
					return;
				}
			}
			System.out.print("Enter No of Products : ");
			numberOfProducts = console.nextInt();
		}

		totalAmount = calculateTotal(productName, numberOfProducts);
		System.err.println("\nTotal Amount is  : " + totalAmount + "\n");

		String request = createXACMLRequest(userName, productName,
				numberOfProducts, totalAmount);
		// String request = createXACMLRequest("bob", "Food", 2, 40);
		PDP pdp = getPDPNewInstance();

		System.out
				.println("\n======================== XACML Request ====================");
		System.out.println(request);
		System.out
				.println("===========================================================");

		String response = pdp.evaluate(request);

		System.out
				.println("\n======================== XACML Response ===================");
		System.out.println(response);
		System.out
				.println("===========================================================");

		try {
			ResponseCtx responseCtx = ResponseCtx
					.getInstance(getXacmlResponse(response));
			AbstractResult result = responseCtx.getResults().iterator().next();
			if (AbstractResult.DECISION_PERMIT == result.getDecision()) {
				System.out.println("\n" + userName
						+ " is authorized to perform this purchase\n\n");
			} else {
				System.out.println("\n" + userName
						+ " is NOT authorized to perform this purchase\n");
				List<Advice> advices = result.getAdvices();
				for (Advice advice : advices) {
					List<AttributeAssignment> assignments = advice
							.getAssignments();
					for (AttributeAssignment assignment : assignments) {
						System.out.println("Advice :  "
								+ assignment.getContent() + "\n\n");
					}
				}
			}
		} catch (ParsingException e) {
			e.printStackTrace();
		}

	}

	private static void initData() {

		idMap.put("1", "Food");
		idMap.put("2", "Drink");
		idMap.put("3", "Fruit");
		idMap.put("4", "Liquor");
		idMap.put("5", "Medicine");

		priceMap.put("Food", "20");
		priceMap.put("Drink", "5");
		priceMap.put("Fruit", "15");
		priceMap.put("Liquor", "80");
		priceMap.put("Medicine", "50");
	}

	private static void initBalana() {

		try {
			// using file based policy repository. so set the policy location as
			// system property
			String policyLocation = (new File(".")).getCanonicalPath()
					+ File.separator + "trading-sample-policies";
			System.setProperty(FileBasedPolicyFinderModule.POLICY_DIR_PROPERTY,
					policyLocation);
		} catch (IOException e) {
			System.err.println("Can not locate policy repository");
		}
		// create default instance of Balana
		balana = Balana.getInstance();
	}

	/**
	 * Returns a new PDP instance with new XACML policies
	 * 
	 * @return a PDP instance
	 */
	private static PDP getPDPNewInstance() {

		PDPConfig pdpConfig = balana.getPdpConfig();

		// registering new attribute finder. so default PDPConfig is needed to
		// change
		AttributeFinder attributeFinder = pdpConfig.getAttributeFinder();
		List<AttributeFinderModule> finderModules = attributeFinder
				.getModules();
		finderModules.add(new SampleAttributeFinderModule());
		attributeFinder.setModules(finderModules);

		return new PDP(new PDPConfig(attributeFinder,
				pdpConfig.getPolicyFinder(), null, true));
	}

	public static int calculateTotal(String productName, int amount) {

		String priceString = priceMap.get(productName);
		return Integer.parseInt(priceString) * amount;

	}

	/**
	 * Creates DOM representation of the XACML request
	 * 
	 * @param response
	 *            XACML request as a String object
	 * @return XACML request as a DOM element
	 */
	public static Element getXacmlResponse(String response) {

		ByteArrayInputStream inputStream;
		DocumentBuilderFactory dbf;
		Document doc;

		inputStream = new ByteArrayInputStream(response.getBytes());
		dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);

		try {
			doc = dbf.newDocumentBuilder().parse(inputStream);
		} catch (Exception e) {
			System.err
					.println("DOM of request element can not be created from String");
			return null;
		} finally {
			try {
				inputStream.close();
			} catch (IOException e) {
				System.err
						.println("Error in closing input stream of XACML response");
			}
		}
		return doc.getDocumentElement();
	}

	public static void printDescription() {

		System.out
				.println("\nK-Market is on-line trading company. They have implemented some access "
						+ "control over the on-line trading using XACML policies. K-Martket has separated their "
						+ "customers in to three groups and has put limit on on-line buying items.\n");

	}

	public static String createXACMLRequest(String userName, String resource,
			int amount, int totalAmount) {

		return "<Request xmlns=\"urn:oasis:names:tc:xacml:3.0:core:schema:wd-17\" CombinedDecision=\"false\" ReturnPolicyIdList=\"false\">\n"
				+ "<Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:action\">\n"
				+ "<Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:action:action-id\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">buy</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n"
				+ "<Attributes Category=\"urn:oasis:names:tc:xacml:1.0:subject-category:access-subject\">\n"
				+ "<Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:subject:subject-id\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">"
				+ userName
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n"
				+ "<Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:resource\">\n"
				+ "<Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:resource:resource-id\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">"
				+ resource
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n"
				+ "<Attributes Category=\"http://kmarket.com/category\">\n"
				+ "<Attribute AttributeId=\"http://kmarket.com/id/amount\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#integer\">"
				+ amount
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "<Attribute AttributeId=\"http://kmarket.com/id/totalAmount\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#integer\">"
				+ totalAmount
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n" + "</Request>";

	}
}
